//
// System.DateTime.cs
//
// Authors:
//   Marcel Narings (marcel@narings.nl)
//   Martin Baulig (martin@gnome.org)
//   Atsushi Enomoto (atsushi@ximian.com)
//   Marek Safar (marek.safar@gmail.com)
//
//   (C) 2001 Marcel Narings
// Copyright (C) 2004-2006 Novell, Inc (http://www.novell.com)
// Copyright (C) 2012 Xamarin Inc (http://www.xamarin.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
// 
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

#include "DateTime.h"

#include <System/Globalization/Calendar.h>
#include <System/Globalization/CultureInfo.h>
#include <System/Collections/Generic/list.h>
#include <System/TimeZone.h>
#include <System/Math.h>
#include <System/Int64.h>
#include <System/Char.h>
#include <System/ArgumentOutOfRangeException.h>
#include <System/FormatException.h> // TODO: Implementera FormatException.h :)

using namespace Riccsson::System;
using namespace Riccsson::System::Collections::Generic;

readonly int DateTime::daysmonth[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
readonly int DateTime::daysmonthleap[13] = { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

PROP3GET_STATIC_INIT(DateTime, DateTime, Now)
PROP3GET_STATIC_INIT(DateTime, DateTime, Today)
PROP3GET_STATIC_INIT(DateTime, DateTime, UtcNow)


readonly DateTime DateTime::MaxValue;
readonly DateTime DateTime::MinValue;

readonly char* DateTime::ParseTimeFormats[17];
readonly char* DateTime::ParseYearDayMonthFormats[10];
readonly char* DateTime::ParseYearMonthDayFormats[12];
readonly char* DateTime::ParseDayMonthYearFormats[15];
readonly char* DateTime::ParseMonthDayYearFormats[15];
readonly char* DateTime::ParseGenericYearMonthDayFormats[7];
readonly char* DateTime::MonthDayShortFormats[3];
readonly char* DateTime::DayMonthShortFormats[3];

const double DateTime::OAMinValue = -657435.0;
const double DateTime::OAMaxValue = 2958466.0;

//
// To reduce the time consumed by DateTime.Now, we keep
// the difference to map the system time into a local
// time into `to_local_time_span', we record the timestamp
// for this in `last_now'
//
TimeSpan DateTime::to_local_time_span_object;
long long DateTime::last_now;

REGISTER_CLASS_CPP(DateTime)
{
	ParseTimeFormats[0] = "H:m:s.fff zzz";
	ParseTimeFormats[1] = "H:m:s.fffffffzzz";
	ParseTimeFormats[2] = "H:m:s.fffffff";
	ParseTimeFormats[3] = "H:m:s.ffffff";
	ParseTimeFormats[4] = "H:m:s.ffffffzzz";
	ParseTimeFormats[5] = "H:m:s.fffff";
	ParseTimeFormats[6] = "H:m:s.ffff";
	ParseTimeFormats[7] = "H:m:s.fff";
	ParseTimeFormats[8] = "H:m:s.ff";
	ParseTimeFormats[9] = "H:m:s.f";
	ParseTimeFormats[10] = "H:m:s tt zzz";
	ParseTimeFormats[11] = "H:m:szzz";
	ParseTimeFormats[12] = "H:m:s";
	ParseTimeFormats[13] = "H:mzzz";
	ParseTimeFormats[14] = "H:m";
	ParseTimeFormats[15] = "H tt";
	ParseTimeFormats[16] = "H'\u6642'm'\u5206's'\u79D2'";

	ParseYearDayMonthFormats[0]	= "yyyy/M/dT";
    ParseYearDayMonthFormats[1] = "M/yyyy/dT";
    ParseYearDayMonthFormats[2] = "yyyy'\u5E74'M'\u6708'd'\u65E5";
    ParseYearDayMonthFormats[3] = "yyyy/d/MMMM";
    ParseYearDayMonthFormats[4] = "yyyy/MMM/d";
    ParseYearDayMonthFormats[5] = "d/MMMM/yyyy";
    ParseYearDayMonthFormats[6] = "MMM/d/yyyy";
    ParseYearDayMonthFormats[7] = "d/yyyy/MMMM";
    ParseYearDayMonthFormats[8] = "MMM/yyyy/d";
    ParseYearDayMonthFormats[9] = "yy/d/M";

	ParseYearMonthDayFormats[0]	= "yyyy/M/dT";
    ParseYearMonthDayFormats[1] = "M/yyyy/dT";
    ParseYearMonthDayFormats[2] = "yyyy'\u5E74'M'\u6708'd'\u65E5";
    ParseYearMonthDayFormats[3] = "yyyy/MMMM/d";
    ParseYearMonthDayFormats[4] = "yyyy/d/MMM";
    ParseYearMonthDayFormats[5] = "MMMM/d/yyyy";
    ParseYearMonthDayFormats[6] = "d/MMM/yyyy";
    ParseYearMonthDayFormats[7] = "MMMM/yyyy/d";
    ParseYearMonthDayFormats[8] = "d/yyyy/MMM";
    ParseYearMonthDayFormats[9] = "yy/MMMM/d";
    ParseYearMonthDayFormats[10] = "yy/d/MMM";
    ParseYearMonthDayFormats[11] = "MMM/yy/d";

    ParseDayMonthYearFormats[0]	= "yyyy/M/dT";
    ParseDayMonthYearFormats[1] = "M/yyyy/dT";
    ParseDayMonthYearFormats[2] = "yyyy'\u5E74'M'\u6708'd'\u65E5";
    ParseDayMonthYearFormats[3] = "yyyy/MMMM/d";
    ParseDayMonthYearFormats[4] = "yyyy/d/MMM";
    ParseDayMonthYearFormats[5] = "d/MMMM/yyyy";
    ParseDayMonthYearFormats[6] = "MMM/d/yyyy";
    ParseDayMonthYearFormats[7] = "MMMM/yyyy/d";
    ParseDayMonthYearFormats[8] = "d/yyyy/MMM",
    ParseDayMonthYearFormats[9] = "d/MMMM/yy";
    ParseDayMonthYearFormats[10] = "yy/MMM/d";
    ParseDayMonthYearFormats[11] = "d/yy/MMM";
    ParseDayMonthYearFormats[12] = "yy/d/MMM";
    ParseDayMonthYearFormats[13] = "MMM/d/yy";
    ParseDayMonthYearFormats[14] = "MMM/yy/d";

    ParseMonthDayYearFormats[0]	= "yyyy/M/dT";
    ParseMonthDayYearFormats[1] = "M/yyyy/dT";
    ParseMonthDayYearFormats[2] = "yyyy'\u5E74'M'\u6708'd'\u65E5";
    ParseMonthDayYearFormats[3] = "yyyy/MMMM/d";
    ParseMonthDayYearFormats[4] = "yyyy/d/MMM";
    ParseMonthDayYearFormats[5] = "MMMM/d/yyyy";
    ParseMonthDayYearFormats[6] = "d/MMM/yyyy";
    ParseMonthDayYearFormats[7] = "MMMM/yyyy/d";
    ParseMonthDayYearFormats[8] = "d/yyyy/MMM";
    ParseMonthDayYearFormats[9] = "MMMM/d/yy";
    ParseMonthDayYearFormats[10] = "MMM/yy/d";
    ParseMonthDayYearFormats[11] = "d/MMM/yy";
    ParseMonthDayYearFormats[12] = "yy/MMM/d";
    ParseMonthDayYearFormats[13] = "d/yy/MMM";
    ParseMonthDayYearFormats[14] = "yy/d/MMM";

    ParseGenericYearMonthDayFormats[0] = "yyyy/M/dT";
    ParseGenericYearMonthDayFormats[1] = "yyyy/M/d";
    ParseGenericYearMonthDayFormats[2] = "M/yyyy/dT";
    ParseGenericYearMonthDayFormats[3] = "M/yyyy/d";
    ParseGenericYearMonthDayFormats[4] = "yyyy'\u5E74'M'\u6708'd'\u65E5";
    ParseGenericYearMonthDayFormats[5] = "yyyy'-'M'-'dT";
    ParseGenericYearMonthDayFormats[6] = "yyyy'-'M'-'d";

    MonthDayShortFormats[0] = "MMMM/d";
    MonthDayShortFormats[1] = "d/MMM";
    MonthDayShortFormats[2] = "yyyy/MMMM";

    DayMonthShortFormats[0] = "d/MMMM";
    DayMonthShortFormats[1] = "MMM/yy";
    DayMonthShortFormats[2] = "yyyy/MMMM";
}

//---------------------------------------------------------------------------------------
int DateTime::AbsoluteDays (int year, int month, int day)
{
	const int* days;
	int temp = 0, m=1 ;
                
	days = (IsLeapYear(year) ? daysmonthleap  : daysmonth);
                        
	while (m < month)
		temp += days[m++];

	return ((day-1) + temp + (365* (year-1)) + ((year-1)/4) - ((year-1)/100) + ((year-1)/400));
}
					 
//---------------------------------------------------------------------------------------
int DateTime::FromTicks(Which what)
{
	int num400, num100, num4, numyears; 
    int M =1;

    const int* days = daysmonth;
    int totaldays = (int) ((encoded & TicksMask) / TimeSpan::TicksPerDay);

    num400 = (totaldays / dp400);
    totaldays -=  num400 * dp400;
                
    num100 = (totaldays / dp100);
    if (num100 == 4)   // leap
		num100 = 3;
    totaldays -= (num100 * dp100);

    num4 = totaldays / dp4;
    totaldays -= (num4 * dp4);

    numyears = totaldays / 365 ;

    if (numyears == 4)  //leap
		numyears =3 ;
    if (what == Which::Year )
		return num400*400 + num100*100 + num4*4 + numyears + 1;

    totaldays -= (numyears * 365) ;
    if (what == Which::DayYear )
		return totaldays + 1;
                        
    if((numyears==3) && ((num100 == 3) || !(num4 == 24)) ) //31 dec leapyear
		days = daysmonthleap;

    while (totaldays >= days[M])
		totaldays -= days[M++];

    if (what == Which::Month )
		return M;

    return totaldays +1; 
}
					 
//---------------------------------------------------------------------------------------
void DateTime::InvalidTickValue (long long ticks)
{
	//string msg = Locale.GetText ("Value {0} is outside the valid range [0,{1}].", ticks, MAX_VALUE_TICKS);
	//throw new ArgumentOutOfRangeException ("ticks", msg);
	throw;
}
			
//---------------------------------------------------------------------------------------
void DateTime::init(int year, int month, int day, int hour, int minute, int second, int millisecond)
{
	if (year < 1 || year > 9999 || 
		month < 1 || month >12  ||
		day < 1 || day > DaysInMonth(year, month) ||
		hour < 0 || hour > 23 ||
		minute < 0 || minute > 59 ||
		second < 0 || second > 59 ||
		millisecond < 0 || millisecond > 999)
			throw ArgumentOutOfRangeException( string("Parameters describe an ") + "unrepresentable DateTime.");

	encoded = TimeSpan(AbsoluteDays(year,month,day), hour, minute, second, millisecond).Ticks;
}
					 
//---------------------------------------------------------------------------------------
void DateTime::init(int year, int month, int day, int hour, int minute, int second, int millisecond, Globalization::Calendar* calendar)
{
	if (calendar == null)
	throw ArgumentNullException("calendar");
	encoded = calendar->ToDateTime (year, month, day, hour, minute, second, millisecond).encoded;
}

DateTime::DateTime()
	: PROP3GET_INIT(DateTime, Date)
	, PROP3GET_INIT(DateTime, Day)
	, PROP3GET_INIT(DateTime, dayOfWeek)
	, PROP3GET_INIT(DateTime, DayOfYear)
	, PROP3GET_INIT(DateTime, Hour)
	, PROP3GET_INIT(DateTime, Kind)
	, PROP3GET_INIT(DateTime, Millisecond)
	, PROP3GET_INIT(DateTime, Minute)
	, PROP3GET_INIT(DateTime, Month)
	, PROP3GET_INIT(DateTime, Second)
	, PROP3GET_INIT(DateTime, Ticks)
	, PROP3GET_INIT(DateTime, TimeOfDay)
	, PROP3GET_INIT(DateTime, Year)
{

}
DateTime::DateTime(long long ticks)
	: PROP3GET_INIT(DateTime, Date)
	, PROP3GET_INIT(DateTime, Day)
	, PROP3GET_INIT(DateTime, dayOfWeek)
	, PROP3GET_INIT(DateTime, DayOfYear)
	, PROP3GET_INIT(DateTime, Hour)
	, PROP3GET_INIT(DateTime, Kind)
	, PROP3GET_INIT(DateTime, Millisecond)
	, PROP3GET_INIT(DateTime, Minute)
	, PROP3GET_INIT(DateTime, Month)
	, PROP3GET_INIT(DateTime, Second)
	, PROP3GET_INIT(DateTime, Ticks)
	, PROP3GET_INIT(DateTime, TimeOfDay)
	, PROP3GET_INIT(DateTime, Year)
{
	if (ticks < 0 || ticks > MAX_VALUE_TICKS)
		InvalidTickValue(ticks);
                
	encoded = ticks;
}
DateTime::DateTime(long long ticks, DateTimeKind kind)
	: PROP3GET_INIT(DateTime, Date)
	, PROP3GET_INIT(DateTime, Day)
	, PROP3GET_INIT(DateTime, dayOfWeek)
	, PROP3GET_INIT(DateTime, DayOfYear)
	, PROP3GET_INIT(DateTime, Hour)
	, PROP3GET_INIT(DateTime, Kind)
	, PROP3GET_INIT(DateTime, Millisecond)
	, PROP3GET_INIT(DateTime, Minute)
	, PROP3GET_INIT(DateTime, Month)
	, PROP3GET_INIT(DateTime, Second)
	, PROP3GET_INIT(DateTime, Ticks)
	, PROP3GET_INIT(DateTime, TimeOfDay)
	, PROP3GET_INIT(DateTime, Year)
{
	if (ticks < 0 || ticks > MAX_VALUE_TICKS)
		InvalidTickValue (ticks);

	if (kind > DateTimeKind::Local)
		throw ArgumentException("Invalid DateTimeKind value.", "kind");

	encoded = ((long long)kind << KindShift) | ticks;
}
DateTime::DateTime(int year, int month, int day)
	: PROP3GET_INIT(DateTime, Date)
	, PROP3GET_INIT(DateTime, Day)
	, PROP3GET_INIT(DateTime, dayOfWeek)
	, PROP3GET_INIT(DateTime, DayOfYear)
	, PROP3GET_INIT(DateTime, Hour)
	, PROP3GET_INIT(DateTime, Kind)
	, PROP3GET_INIT(DateTime, Millisecond)
	, PROP3GET_INIT(DateTime, Minute)
	, PROP3GET_INIT(DateTime, Month)
	, PROP3GET_INIT(DateTime, Second)
	, PROP3GET_INIT(DateTime, Ticks)
	, PROP3GET_INIT(DateTime, TimeOfDay)
	, PROP3GET_INIT(DateTime, Year)
{
	init(year, month, day, 0, 0, 0, 0);
}
DateTime::DateTime(int year, int month, int day, Globalization::Calendar* calendar)
	: PROP3GET_INIT(DateTime, Date)
	, PROP3GET_INIT(DateTime, Day)
	, PROP3GET_INIT(DateTime, dayOfWeek)
	, PROP3GET_INIT(DateTime, DayOfYear)
	, PROP3GET_INIT(DateTime, Hour)
	, PROP3GET_INIT(DateTime, Kind)
	, PROP3GET_INIT(DateTime, Millisecond)
	, PROP3GET_INIT(DateTime, Minute)
	, PROP3GET_INIT(DateTime, Month)
	, PROP3GET_INIT(DateTime, Second)
	, PROP3GET_INIT(DateTime, Ticks)
	, PROP3GET_INIT(DateTime, TimeOfDay)
	, PROP3GET_INIT(DateTime, Year)
{
	init(year, month, day, 0, 0, 0, 0, calendar);
}
DateTime::DateTime(int year, int month, int day, int hour, int minute, int second)
	: PROP3GET_INIT(DateTime, Date)
	, PROP3GET_INIT(DateTime, Day)
	, PROP3GET_INIT(DateTime, dayOfWeek)
	, PROP3GET_INIT(DateTime, DayOfYear)
	, PROP3GET_INIT(DateTime, Hour)
	, PROP3GET_INIT(DateTime, Kind)
	, PROP3GET_INIT(DateTime, Millisecond)
	, PROP3GET_INIT(DateTime, Minute)
	, PROP3GET_INIT(DateTime, Month)
	, PROP3GET_INIT(DateTime, Second)
	, PROP3GET_INIT(DateTime, Ticks)
	, PROP3GET_INIT(DateTime, TimeOfDay)
	, PROP3GET_INIT(DateTime, Year)
{
	init(year, month, day, hour, minute, second, 0);
}
DateTime::DateTime(int year, int month, int day, int hour, int minute, int second, Globalization::Calendar* calendar)
	: PROP3GET_INIT(DateTime, Date)
	, PROP3GET_INIT(DateTime, Day)
	, PROP3GET_INIT(DateTime, dayOfWeek)
	, PROP3GET_INIT(DateTime, DayOfYear)
	, PROP3GET_INIT(DateTime, Hour)
	, PROP3GET_INIT(DateTime, Kind)
	, PROP3GET_INIT(DateTime, Millisecond)
	, PROP3GET_INIT(DateTime, Minute)
	, PROP3GET_INIT(DateTime, Month)
	, PROP3GET_INIT(DateTime, Second)
	, PROP3GET_INIT(DateTime, Ticks)
	, PROP3GET_INIT(DateTime, TimeOfDay)
	, PROP3GET_INIT(DateTime, Year)
{
	init(year, month, day, hour, minute, second, 0, calendar);
}
DateTime::DateTime(int year, int month, int day, int hour, int minute, int second, DateTimeKind kind)
	: PROP3GET_INIT(DateTime, Date)
	, PROP3GET_INIT(DateTime, Day)
	, PROP3GET_INIT(DateTime, dayOfWeek)
	, PROP3GET_INIT(DateTime, DayOfYear)
	, PROP3GET_INIT(DateTime, Hour)
	, PROP3GET_INIT(DateTime, Kind)
	, PROP3GET_INIT(DateTime, Millisecond)
	, PROP3GET_INIT(DateTime, Minute)
	, PROP3GET_INIT(DateTime, Month)
	, PROP3GET_INIT(DateTime, Second)
	, PROP3GET_INIT(DateTime, Ticks)
	, PROP3GET_INIT(DateTime, TimeOfDay)
	, PROP3GET_INIT(DateTime, Year)
{
	init(year, month, day, hour, minute, second, 0);

	if (kind > DateTimeKind::Local)
		throw ArgumentException ("Invalid DateTimeKind value.", "kind");
    encoded |= ((long long)kind << KindShift);
}
DateTime::DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond)
	: PROP3GET_INIT(DateTime, Date)
	, PROP3GET_INIT(DateTime, Day)
	, PROP3GET_INIT(DateTime, dayOfWeek)
	, PROP3GET_INIT(DateTime, DayOfYear)
	, PROP3GET_INIT(DateTime, Hour)
	, PROP3GET_INIT(DateTime, Kind)
	, PROP3GET_INIT(DateTime, Millisecond)
	, PROP3GET_INIT(DateTime, Minute)
	, PROP3GET_INIT(DateTime, Month)
	, PROP3GET_INIT(DateTime, Second)
	, PROP3GET_INIT(DateTime, Ticks)
	, PROP3GET_INIT(DateTime, TimeOfDay)
	, PROP3GET_INIT(DateTime, Year)
{
	init(year, month, day, hour, minute, second, millisecond);
}
DateTime::DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, Globalization::Calendar* calendar)
	: PROP3GET_INIT(DateTime, Date)
	, PROP3GET_INIT(DateTime, Day)
	, PROP3GET_INIT(DateTime, dayOfWeek)
	, PROP3GET_INIT(DateTime, DayOfYear)
	, PROP3GET_INIT(DateTime, Hour)
	, PROP3GET_INIT(DateTime, Kind)
	, PROP3GET_INIT(DateTime, Millisecond)
	, PROP3GET_INIT(DateTime, Minute)
	, PROP3GET_INIT(DateTime, Month)
	, PROP3GET_INIT(DateTime, Second)
	, PROP3GET_INIT(DateTime, Ticks)
	, PROP3GET_INIT(DateTime, TimeOfDay)
	, PROP3GET_INIT(DateTime, Year)
{
	init(year, month, day, hour, minute, second, millisecond, calendar);
}
DateTime::DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, DateTimeKind kind)
	: PROP3GET_INIT(DateTime, Date)
	, PROP3GET_INIT(DateTime, Day)
	, PROP3GET_INIT(DateTime, dayOfWeek)
	, PROP3GET_INIT(DateTime, DayOfYear)
	, PROP3GET_INIT(DateTime, Hour)
	, PROP3GET_INIT(DateTime, Kind)
	, PROP3GET_INIT(DateTime, Millisecond)
	, PROP3GET_INIT(DateTime, Minute)
	, PROP3GET_INIT(DateTime, Month)
	, PROP3GET_INIT(DateTime, Second)
	, PROP3GET_INIT(DateTime, Ticks)
	, PROP3GET_INIT(DateTime, TimeOfDay)
	, PROP3GET_INIT(DateTime, Year)
{
	init(year, month, day, hour, minute, second, millisecond);

	if (kind > DateTimeKind::Local)
		throw ArgumentException ("Invalid DateTimeKind value.", "kind");
    encoded |= ((long long)kind << KindShift);
}
DateTime::DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, Globalization::Calendar* calendar, DateTimeKind kind)
	: PROP3GET_INIT(DateTime, Date)
	, PROP3GET_INIT(DateTime, Day)
	, PROP3GET_INIT(DateTime, dayOfWeek)
	, PROP3GET_INIT(DateTime, DayOfYear)
	, PROP3GET_INIT(DateTime, Hour)
	, PROP3GET_INIT(DateTime, Kind)
	, PROP3GET_INIT(DateTime, Millisecond)
	, PROP3GET_INIT(DateTime, Minute)
	, PROP3GET_INIT(DateTime, Month)
	, PROP3GET_INIT(DateTime, Second)
	, PROP3GET_INIT(DateTime, Ticks)
	, PROP3GET_INIT(DateTime, TimeOfDay)
	, PROP3GET_INIT(DateTime, Year)
{
	init(year, month, day, hour, minute, second, millisecond, calendar);

	if (kind > DateTimeKind::Local)
		throw ArgumentException ("Invalid DateTimeKind value.", "kind");
    encoded |= ((long long)kind << KindShift);
}
PROP3_GET_CPP(DateTime, GC_PTR<DateTime>, Date)
{
	GC_PTR<DateTime> ret = new DateTime(Year, Month, Day);
    ret->encoded |= encoded & KindMask;
    return ret;

}
PROP3_GET_CPP(DateTime, int, Day)
{
	return FromTicks(Which::Day); 
}
PROP3_GET_CPP(DateTime, DayOfWeek, dayOfWeek)
{
	return (DayOfWeek)((((encoded & TicksMask)/TimeSpan::TicksPerDay)+1) % 7);
}
PROP3_GET_CPP(DateTime, int, DayOfYear)
{
	return FromTicks(Which::DayYear); 
}
PROP3_GET_CPP(DateTime, int, Hour)
{
	return (int)((encoded & TicksMask) % TimeSpan::TicksPerDay / TimeSpan::TicksPerHour);
}
PROP3_GET_CPP(DateTime, DateTimeKind, Kind)
{
	 return (DateTimeKind)((ulong)encoded >> KindShift);
}
PROP3_GET_CPP(DateTime, int, Millisecond)
{
	return (int)((encoded & TicksMask) % TimeSpan::TicksPerSecond / TimeSpan::TicksPerMillisecond);
}
PROP3_GET_CPP(DateTime, int, Minute)
{
	return (int)((encoded & TicksMask) % TimeSpan::TicksPerHour / TimeSpan::TicksPerMinute);
}
PROP3_GET_CPP(DateTime, int, Month)
{
	return FromTicks(Which::Month); 
}
PROP3_GET_CPP(DateTime, DateTime, Now)
{
	// TODO: This is litle ugly. TimeZones static properties will be created when someone run CurrentTimeZone first time...
	//		 We can to run that property in another startup initializer instead.
	GC_PTR<TimeZone> currentTimeZone = TimeZone::CurrentTimeZone;

	long long now = GetNow();
    DateTime dt = DateTime(now);

    if (Math::Abs(now - last_now) > TimeSpan::TicksPerMinute)
	{
		to_local_time_span_object = TimeZone::currentTimeZone->GetLocalTimeDiff(dt);
		last_now = now;
    }
	
    DateTime ret = dt + to_local_time_span_object;
    ret.encoded |= ((long long)DateTimeKind::Local << KindShift);
    return ret;

}
PROP3_GET_CPP(DateTime, int, Second)
{
	return (int)((encoded & TicksMask) % TimeSpan::TicksPerMinute / TimeSpan::TicksPerSecond);
}
PROP3_GET_CPP(DateTime, long long, Ticks)
{
	return encoded & TicksMask;
}
PROP3_GET_CPP(DateTime, TimeSpan, TimeOfDay)
{
	return TimeSpan((encoded & TicksMask) % TimeSpan::TicksPerDay);
}
PROP3_GET_CPP(DateTime, DateTime, Today)
{
	DateTime now = Now;
    DateTime today = DateTime(now.Year, now.Month, now.Day);
    today.encoded |= ((long long)DateTimeKind::Local << KindShift);
    return today;
}
PROP3_GET_CPP(DateTime, DateTime, UtcNow)
{
	return DateTime (GetNow(), DateTimeKind::Utc);
}
PROP3_GET_CPP(DateTime, int, Year)
{
	return FromTicks(Which::Year); 
}
DateTime DateTime::Add(TimeSpan value)
{
	DateTime ret = AddTicks(value.Ticks);
    return ret;
}
DateTime DateTime::AddDays(double value)
{
	return AddMilliseconds(Math::Round(value * 86400000));
}
DateTime DateTime::AddHours(double value)
{
	return AddMilliseconds(value * 3600000);
}
DateTime DateTime::AddMilliseconds(double value)
{
	if ((value * TimeSpan::TicksPerMillisecond) > Int64::MaxValue ||
        (value * TimeSpan::TicksPerMillisecond) < Int64::MinValue)
	{
            throw ArgumentOutOfRangeException();
    }
    long long msticks = (long long)Math::Round (value * TimeSpan::TicksPerMillisecond);

    return AddTicks (msticks);

}
DateTime DateTime::AddMinutes(double value)
{
	return AddMilliseconds(value * 60000);
}
DateTime DateTime::AddMonths(int months)
{
	int day, month, year,  maxday;
    DateTime temp;


    day = this->Day;
    month = this->Month + (months % 12);
    year = this->Year + months/12 ;
                        
    if (month < 1)
    {
            month = 12 + month ;
            year -- ;
    }
    else if (month>12) 
    {
            month = month -12;
            year ++;
    }
    maxday = DaysInMonth(year, month);
    if (day > maxday)
            day = maxday;


    temp = DateTime(year, month, day);
    temp.encoded |= encoded & KindMask;
    return  temp.Add (this->TimeOfDay);
}
DateTime DateTime::AddSeconds(double value)
{
	return AddMilliseconds(value * 1000);
}
DateTime DateTime::AddTicks(long long value)
{
	long long res = value + (encoded & TicksMask);
    if (res < 0 || res > MAX_VALUE_TICKS)
		throw ArgumentOutOfRangeException();


    DateTime ret = DateTime(res);
    ret.encoded |= (encoded & KindMask);
    return ret;
}
DateTime DateTime::AddYears(int value)
{
	return AddMonths(value * 12);
}
int DateTime::Compare(DateTime t1, DateTime t2)
{
	long long t1t = t1.encoded & TicksMask;
    long long t2t = t2.encoded & TicksMask;
                        
    if (t1t < t2t) 
		return -1;
    else if (t1t > t2t) 
		return 1;
    else
		return 0;
}
int DateTime::CompareTo(DateTime* value)
{
	if (value == null)
		return 1;

    if (!(instanceof(value, System::DateTime)))
		throw ArgumentException ( "Value is not a System.DateTime" );

    return Compare(*this, (DateTime)*value);
}
int DateTime::CompareTo(object* value)
{
	if (value == null)
		return 1;

    if (!(instanceof(value, System::DateTime)))
		throw ArgumentException ( "Value is not a System.DateTime" );

    return Compare(*this, *(DateTime*)value);
}
int DateTime::DaysInMonth(int year, int month)
{
	const int* days;

    if (month < 1 || month >12)
		throw ArgumentOutOfRangeException();

    if (year < 1 || year > 9999)
		throw ArgumentOutOfRangeException();

    days = (IsLeapYear(year) ? daysmonthleap : daysmonth);
    return days[month];
}
bool DateTime::Equals(DateTime* value)
{
	return (value->encoded & TicksMask) == (encoded & TicksMask);
}
bool DateTime::Equals(object* value)
{
	if (!(instanceof(value, System::DateTime)))
		return false;

    return (((DateTime*)value)->encoded & TicksMask) == (encoded & TicksMask);
}
bool DateTime::Equals(DateTime t1, DateTime t2)
{
	return (t1.encoded & TicksMask) == (t2.encoded & TicksMask);
}
DateTime DateTime::FromBinary(long long dateData)
{
	switch ((ulong)dateData >> KindShift)
	{
    case 1: // Utc
		return DateTime(dateData & TicksMask, DateTimeKind::Utc);
    case 0: // Unspecified
		return DateTime(dateData, DateTimeKind::Unspecified);
    default: // Local
		return DateTime(dateData & TicksMask, DateTimeKind::Utc).ToLocalTime();
    }
}
DateTime DateTime::FromFileTime(long long fileTime)
{
	if (fileTime < 0)
		throw ArgumentOutOfRangeException("fileTime", "< 0");

	return DateTime(w32file_epoch + fileTime).ToLocalTime ();
}
DateTime DateTime::FromFileTimeUtc(long long fileTime)
{
	if (fileTime < 0)
		throw ArgumentOutOfRangeException ("fileTime", "< 0");

    return DateTime(w32file_epoch + fileTime, DateTimeKind::Utc);
}
DateTime DateTime::FromOADate(double d)
{
	throw;
}
GC_PTR<Array<GC_PTR<string>>> DateTime::GetDateTimeFormats()
{
	return GetDateTimeFormats(Globalization::CultureInfo::CurrentCulture.get());
}
GC_PTR<Array<GC_PTR<string>>> DateTime::GetDateTimeFormats(char format)
{
	if (string("dDgGfFmMrRstTuUyY").IndexOf(format) < 0)
		 throw FormatException("Invalid format character.");
    GC_PTR<Array<GC_PTR<string>>> result = new Array<GC_PTR<string>>(1);
    (**result)[0] = this->ToString(Char(format).ToString());
    return result;

}
GC_PTR<Array<GC_PTR<string>>> DateTime::GetDateTimeFormats(IFormatProvider* provider)
{
	//DateTimeFormatInfo* info = (DateTimeFormatInfo*)provider->GetFormat(typeof(DateTimeFormatInfo));
	//
    //GC_PTR<List<GC_PTR<string>>> l = new List<GC_PTR<string>>();
    //foreach (char c in "dDgGfFmMrRstTuUyY")
	//	l.AddRange(GetDateTimeFormats (c, info));
	//
    //return l->ToArray();
	throw;
}
GC_PTR<Array<GC_PTR<string>>> DateTime::GetDateTimeFormats(char format, IFormatProvider* provider)
{
	if (string("dDgGfFmMrRstTuUyY").IndexOf(format) < 0)
		throw FormatException("Invalid format character.");

    // LAMESPEC: There is NO assurance that 'U' ALWAYS
    // euqals to 'F', but since we have to iterate all
    // the pattern strings, we cannot just use 
    // ToString("U", provider) here. I believe that the 
    // method's behavior cannot be formalized.
    bool adjustutc = false;

    switch (format)
	{
    case 'U':
//  case 'r':
//  case 'R':
//  case 'u':
        adjustutc = true;
        break;
    }

    //DateTimeFormatInfo* info = (DateTimeFormatInfo*)provider.GetFormat (typeof(DateTimeFormatInfo));
    //return GetDateTimeFormats(adjustutc, info.GetAllRawDateTimePatterns(format), info);
	throw;
}
int DateTime::GetHashCode()
{
	return (int)encoded;
}
TypeCode DateTime::GetTypeCode()
{
	throw;
}
bool DateTime::IsDaylightSavingTime()
{
	throw;
}
bool DateTime::IsLeapYear(int year)
{
	if(year < 1 || year > 9999)
		throw ArgumentOutOfRangeException();
	return ( (year % 4 == 0 && year % 100 != 0) || year % 400 == 0);
}
DateTime DateTime::Parse(string* s)
{
	throw;
}
DateTime DateTime::Parse(string* s, IFormatProvider* provider)
{
	throw;
}
DateTime DateTime::Parse(string* s, IFormatProvider* provider, Globalization::DateTimeStyles styles)
{
	throw;
}
DateTime DateTime::ParseExact(string* s, string* format, IFormatProvider* provider)
{
	throw;
}
DateTime DateTime::ParseExact(string* s, string* format, IFormatProvider* provider, Globalization::DateTimeStyles style)
{
	throw;
}
DateTime DateTime::ParseExact(string* s, Array<string>* formats, IFormatProvider* provider, Globalization::DateTimeStyles style)
{
	throw;
}
DateTime DateTime::SpecifyKind(DateTime value, DateTimeKind kind)
{
	return DateTime(value.Ticks, kind);
}
TimeSpan DateTime::Subtract(DateTime value)
{
	throw;
}
DateTime DateTime::Subtract(TimeSpan value)
{
	throw;
}
long long DateTime::ToBinary()
{
	if ((encoded & ((long long)DateTimeKind::Local << KindShift)) != 0)
		return (long long)((ulong)ToUniversalTime().Ticks | 0x8000000000000000);
                        
    return encoded;
}
long long DateTime::ToFileTime()
{
	throw;
}
long long DateTime::ToFileTimeUtc()
{
	throw;
}
DateTime DateTime::ToLocalTime()
{
	throw;
}
GC_PTR<string> DateTime::ToLongDateString()
{
	throw;
}
GC_PTR<string> DateTime::ToLongTimeString()
{
	throw;
}
double DateTime::ToOADate()
{
	throw;
}
GC_PTR<string> DateTime::ToShortDateString()
{
	throw;
}
GC_PTR<string> DateTime::ToShortTimeString()
{
	throw;
}
GC_PTR<string> DateTime::ToString()
{
	throw;
}
GC_PTR<string> DateTime::ToString(IFormatProvider* provider)
{
	throw;
}
GC_PTR<string> DateTime::ToString(string* format)
{
	throw;
}
GC_PTR<string> DateTime::ToString(string* format, IFormatProvider* provider)
{
	throw;
}
DateTime DateTime::ToUniversalTime()
{
	throw;
}
bool DateTime::TryParse(string* s, DateTime out result)
{
	throw;
}
bool DateTime::TryParse(string* s, IFormatProvider* provider, Globalization::DateTimeStyles styles, DateTime out result)
{
	throw;
}
bool DateTime::TryParseExact(string* s, string* format, IFormatProvider* provider, Globalization::DateTimeStyles style, DateTime out result)
{
	throw;
}
bool DateTime::TryParseExact(string* s, Array<string>* formats, IFormatProvider* provider, Globalization::DateTimeStyles style, DateTime out result)
{
	throw;
}


//RUN_STATIC(DateTime);
